package com.modakai.med.gateway.route;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * 动态路由
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final RouteDefinitionWriter routeDefinitionWriter;
    private final NacosConfigManager nacosConfigManager;

    private final String dataId = "gateway-routes.json";
    private final String group = "DEFAULT_GROUP";

    // 保存更新过的路由id
    private final Set<String> routeIds = new HashSet<>();

    @PostConstruct
    public void init() throws NacosException {
        log.info("DynamicRouteLoader init");
        String configInfo = nacosConfigManager.getConfigService()
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String config) {
                        updateConfigInfo(config);
                    }
                });

        updateConfigInfo(configInfo);
    }

    /**
     * 当监听到路由配置变更时，更新路由信息。
     * 此方法首先解析新的路由配置，然后清除现有的路由定义，最后添加新的路由定义。
     * 这确保了路由配置的同步更新，以反映最新的配置状态。
     *
     * @param configInfo 新的路由配置信息，以JSON数组形式表示。
     */
    private void updateConfigInfo(String configInfo) {
        // 日志记录配置信息更新的开始
        log.info("监听到路由配置变更，{}", configInfo);

        // 1. 解析新的路由配置信息
        // 1 反序列化
        List<RouteDefinition> routeDefinitions = JSONArray.parseArray(configInfo, RouteDefinition.class);

        // 2. 清除旧的路由定义
        // 2 更新前先清空旧路由
        for (String routeId : routeIds) {
            routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();

        // 如果新的路由定义为空，则直接返回，不进行后续操作
        // 判断 routeDefinitions 集合是否为空
        if (CollectionUtils.isEmpty(routeDefinitions)) {
            return;
        }

        // 3. 添加新的路由定义
        // 3 更新路由
        routeDefinitions.forEach(routeDefinition -> {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            routeIds.add(routeDefinition.getId());
        });
    }

}
